package be.selckin.swu.debug;
import be.selckin.swu.model.DiscardingModel;
import com.google.common.collect.Lists;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.List;
public class ModelDetachedChecker extends AbstractRequestCycleListener {
private static final Logger log = LoggerFactory.getLogger(ModelDetachedChecker.class);
@Override
public void onDetach(RequestCycle cycle) {
super.onDetach(cycle);
checkPage(RequestCycle.get().getMetaData(PageTrackingRequestCycleListener.REQUESTED_PAGE));
checkPage(RequestCycle.get().getMetaData(PageTrackingRequestCycleListener.RESPONSE_PAGE));
}
private void checkPage(IRequestablePage requestablePage) {
if (requestablePage instanceof MarkupContainer) {
((MarkupContainer) requestablePage).visitChildren(new IVisitor<Component, Object>() {
@Override
public void component(Component object, IVisit<Object> visit) {
checkFieldsForModels(object, object);
}
});
}
}
private void checkFieldsForModels(Component component, Object object) {
for (Field field : getAllFields(object)) {
try {
Object value = field.get(object);
if (value != null) {
if (value.getClass().isArray()) {
for (Object arrayEl : (Object[]) value) {
if (arrayEl instanceof IModel)
checkModel(component, arrayEl);
}
} else if (value instanceof IModel) {
checkModel(component, value);
}
}
} catch (IllegalAccessException e) {
log.error("failed", e);
}
}
}
private void checkModel(Component component, Object model) {
if (model instanceof LoadableDetachableModel) {
if (checkFieldIsNull("transientModelObject", model, component))
checkBooleanIsFalse("attached", model, component);
}
if (model instanceof DiscardingModel) {
checkFieldIsNull("obj", model, component);
}
log.debug("found model {}", model);
checkFieldsForModels(component, model);
}
private boolean checkBooleanIsFalse(String name, Object model, Component component) {
Field field = getField(name, model);
if (field != null) {
try {
if (field.getBoolean(model)) {
onFoundNonDetached(model, component);
return false;
}
} catch (IllegalAccessException e) {
log.error("failed", e);
}
} else {
log.error("Could not find field {} in {}", name, model);
}
return true;
}
private void onFoundNonDetached(Object model, Component component) {
log.warn("Found non detached model: {} in {}", model, component);
}
private boolean checkFieldIsNull(String name, Object model, Component component) {
Field field = getField(name, model);
if (field != null) {
try {
if (field.get(model) != null) {
onFoundNonDetached(model, component);
return false;
}
} catch (IllegalAccessException e) {
log.error("failed", e);
}
} else {
log.error("Could not find field {} in {}", name, model);
}
return true;
}
private Field getField(String name, Object obj) {
for (Field field : getAllFields(obj))
if (field.getName().equalsIgnoreCase(name))
return field;
return null;
}
private Iterable<Field> getAllFields(Object obj) {
List<Field> fields = Lists.newArrayList();
Class<?> current = obj.getClass();
while (current != null && current != Object.class) {
for (Field field : current.getDeclaredFields()) {
if (!field.isSynthetic()) {
if (!field.isAccessible())
field.setAccessible(true);
fields.add(field);
}
}
current = current.getSuperclass();
}
return fields;
}
}